package edu.kit.csl.pisa.datatypes;

/*
This file is part of the PISA Alignment Tool.

Copyright (C) 2013
Karlsruhe Institute of Technology
Cognitive Systems Lab (CSL)
Felix Stahlberg

PISA is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

PISA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with PISA. If not, see <http://www.gnu.org/licenses/>.
*/

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * A instance of this class stores a set of word-ids with their string 
 * representations. Do not confuse this with a pronunciation dictionary!
 * The target language dictionary maps phonemes to word-ids, not entire
 * word pronunciations. We use this class only because integer identifiers
 * are better to handle then string names.
 * This class uses the GoF design pattern singleton.
 */
public class Dictionary {

	/**
	 * Dictionary identifier for the source language dictionary.
	 */
	public static final int SRC_DICT = 0;
	
	/**
	 * Dictionary identifier for the target language dictionary.
	 */
	public static final int TRGT_DICT = 1;
	
	/*
	 * Mapping between the word strings and the word-ids.
	 */
	private Map<String, Integer> mapping;
	private ArrayList<String> revMapping;
	
	/*
	 * Currently the lowest free word-id. Is incremented when a new unknown
	 * word is added to the dictionary.
	 */
	private int nextFreeId;
	
	/**
	 * Sole constructor. Creates an empty dictionary. This constructor is only
	 * public for testing purposes and maintaining extensibility. For access to
	 * the dictionaries of source and target language, please use
	 * {@link #getSingleton(int)}.
	 * 
	 * @see #getSingleton(int)
	 */
	public Dictionary() {
		mapping = new HashMap<String, Integer>();
		revMapping = new ArrayList<String>();
		mapping.put("NULL", 0);
		revMapping.add("NULL");
		nextFreeId = 1;
	}
	
	/*
	 * Singleton dictionary instances for both dictionaries (src and trgt).
	 */
	private static Dictionary srcDict, trgtDict;
	
	/**
	 * Get the singleton instances (GoF singleton) for the source or target
	 * language.
	 * 
	 * @param dict which dictionary? One of the *_DICT constants. null if there
	 * 			is no such constant
	 */
	public static Dictionary getSingleton(int dict) {
		switch (dict) {
		case SRC_DICT:
			if (srcDict == null) {
				srcDict = new Dictionary();
			}
			return srcDict;
		case TRGT_DICT:
			if (trgtDict == null) {
				trgtDict = new Dictionary();
			}
			return trgtDict;
		}
		return null;
	}
	
	/**
	 * Get the word id by the given word. If the word is unknown, assign a new
	 * word id and add a new entry to the dictionary.
	 * 
	 * @param key string identifier (word or phoneme)
	 */
	public int get(String key) {
		Integer id = mapping.get(key);
		if (id != null) {
			assert revMapping.get(id).equals(key);
			return id;
		}
		mapping.put(key, nextFreeId);
		revMapping.add(key);
		return nextFreeId++;
	}
	
	/**
	 * Get the word as string by the given word id. If the word id is unknown,
	 * return "unknown".
	 * 
	 * @param the word id
	 */
	public String get(int id) {
		if (id >= nextFreeId) {
			return "unknown";
		}
		String key = revMapping.get(id);
		assert id == mapping.get(key);
		return key;
	}
	
	/**
	 * Count words in the dictionary.
	 * 
	 * @return number of words in the dictionary
	 */
	public int size() {
		return nextFreeId;
	}
	
	/**
	 * Remove all entries in the dictionary. This may be useful in test
	 * scenarios.
	 */
	public void clear() {
		mapping.clear();
		revMapping.clear();
		nextFreeId = 0;
	}
}
